package com.nulldev.util.crypto.encryptAPI;

import java.security.SecureRandom;

import com.nulldev.util.crypto.encryptAPI.manage.Encryptor;
import com.nulldev.util.other.ByteUtils;

public class XOROneTime extends Encryptor {
    private SecureRandom random = new SecureRandom();

    /**
     * Generate a One-time pad.
     *
     * @param length - Pad length
     * @return
     */
    public char[] generate(int length) {
        char[] key = new char[length];

        for(int i = 0; i < length; i++) {
            key[i] = getRandUnicode();
        }

        return key;
    }

    /**
     * Get a VALID random unicode character.
     *
     * @return char
     */
    private char getRandUnicode() {
        char c;

        while(!Character.isDefined(c = getRandomUnicode()));

        return c;
    }

    /**
     * Get a random unicode character.
     * This character may or may not be invalid.
     *
     * @return char
     */
    private char getRandomUnicode() {
        return (char) (random.nextInt() & 0xFFFF);
    }

    /**
     * Encode the data by XORing the data to the pad.
     *
     * @param data - Data to be encoded
     * @param pad - Pad to use
     * @return byte[]
     */
    public byte[] encode(byte[] data, char[] pad) {
        final byte[] encoded = new byte[data.length];

        for (int i = 0; i < data.length; i++) {
            encoded[i] = (byte) (data[i] ^ pad[i]);
        }

        return encoded;
    }

    /**
     * Decode the data by XORing the encoded data to
     * the pad.
     *
     * @param encoded - Data to be decoded
     * @param pad - Pad to use
     * @return byte[]
     */
    public byte[] decode(byte[] encoded, char[] pad) {
        final byte[] decoded = new byte[encoded.length];

        for (int i = 0; i < encoded.length; i++) {
            decoded[i] = (byte) (encoded[i] ^ pad[i]);
        }

        return decoded;
    }

	@Override
	public String getEncryptorID() {
		return "encryptAPI.XOR.OneTime";
	}

	@Override
	public String[] supportedEncryptors() {
		return new String[] {"XOR.OneTime"};
	}

	@Override
	public String encrypt(String text, String password) {
		if (password == null) {
			final byte[] enc = encode(text.getBytes(), generate(16));
			return ByteUtils.toBase64(enc);
		}else {
			final byte[] enc = encode(text.getBytes(), password.toCharArray());
			return ByteUtils.toBase64(enc);
		}
	}

	@Override
	public String decrypt(String text, String password) {
		final byte[] dec = ByteUtils.fromBase64(text);
		return new String(decode(dec, password.toCharArray()));
	}

	@Override
	public byte[] encryptBytes(byte[] data, String password) {
		return encode(data, (password == null ? generate(16) : password.toCharArray()));
	}

	@Override
	public byte[] decryptBytes(byte[] data, String password) {
		return decode(data, password.toCharArray());
	}
}
